package com.syyo.admin.config.security.config;

import com.syyo.admin.config.security.domain.JwtUser;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author /
 * token处理对象
 */
@Slf4j
@Component
public class TokenProvider implements InitializingBean {

    private final SecurityProperties properties;//security配置类
    private static final String AUTHORITIES_KEY = "auth";//权限的key值
    private static final String DB_KEY = "db";//数据源
    private Key key;

    public TokenProvider(SecurityProperties properties) {
        this.properties = properties;
    }


    @Override
    public void afterPropertiesSet() {
        //启动项目时，自动生成key
        byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
        this.key = Keys.hmacShaKeyFor(keyBytes);
    }

    /**
     * 创建token
     *
     * @param authentication security上下文对象
     * @return
     */
    public Map<String, String> createToken(Authentication authentication) {

        JwtUser jwtUser = (JwtUser) authentication.getPrincipal();

        Map<String, String> map = new HashMap<>();
        String roles = authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(","));

        //过期时间
        long now = (new Date()).getTime();
        Date validity = new Date(now + properties.getTokenValidityInSeconds());
        String token = Jwts.builder()
                .setSubject(authentication.getName())//用户名
                .claim(AUTHORITIES_KEY, "1,2")//权限字符串,权限过多会导致token变长，所以这里不保存权限，在redis保存，这里只是一个占位符
                .claim(DB_KEY, jwtUser.getDatasourceName())//权限字符串
                .signWith(key, SignatureAlgorithm.HS256)//使用加密
                .setExpiration(validity)//过期时间
                .compact();

        map.put("roles", roles);
        map.put("token", token);
        return map;

    }

    /**
     * 解析token拿到Authentication上下文对象
     *
     * @param token
     * @return
     */
    public Authentication getAuthentication(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(token)
                .getBody();

        Collection<? extends GrantedAuthority> authorities =
                Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());

        User principal = new User(claims.getSubject(), "", authorities);
        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
    }

    /**
     * 解析token拿到parseToken
     *
     * @param token
     * @return
     */
    public String getDb(String token) {
        Claims body = Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(token)
                .getBody();
        return body.get(DB_KEY).toString();
    }


    /**
     * 校验token
     *
     * @param authToken
     * @return
     */
    boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);
            return true;
        } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
            log.info("无效的token");
        } catch (ExpiredJwtException e) {
            log.info("token令牌过期");
        } catch (UnsupportedJwtException e) {
            log.info("不支持jwt令牌");
        } catch (IllegalArgumentException e) {
            log.info("处理程序的jwt令牌压缩无效");
        }
        return false;
    }

    /**
     * 获取token
     *
     * @param request
     * @return
     */
    public String getToken(HttpServletRequest request) {
        //获取token头
        final String requestHeader = request.getHeader(properties.getHeader());
        // "Bearer " 处理token值的头部
        if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
            return requestHeader.substring(7);
        }
        return null;
    }
}
